home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 07 - 1991 / 07.05 May 91 / Multifile source ƒ / mfile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-12-28  |  18.9 KB  |  780 lines  |  [TEXT/MPS ]

  1. /*
  2. **    File mfile.c
  3. **    Source file for MultiFile dialog.
  4. **
  5. **    Copyright © Eric Schlegel 1987, 1988
  6. */
  7.  
  8.  
  9. #include    <Types.h>
  10. #include    <Resources.h>
  11. #include    <memory.h>
  12. #include    <QuickDraw.h>
  13. #include    <Fonts.h>
  14. #include    <Windows.h>
  15. #include    <Menus.h>
  16. #include    <TextEdit.h>
  17. #include    <Dialogs.h>
  18. #include    <Events.h>
  19. #include    <Lists.h>
  20. #include    <Packages.h>
  21. #include    <Controls.h>
  22. #include    <Files.h>
  23. #include    <Strings.h>
  24. #include    <osutils.h>
  25. #include    <string.h>
  26. #include    "mfpriv.h"
  27.  
  28.  
  29. #define INITMSG        (-1)            /* msg to dlgHook to init itself     */
  30. #define NULLMSG        (100)            /* msg saying nothing happened        */
  31. #define DIRMSG        (102)            /* msg saying hit in cur dir popup    */
  32. #define FOLDMSG        (103)            /* msg to open a folder                */
  33. #define GETDISK        (4)                /* item number of disk name            */
  34. #define DONE_BTN    (11)            /* "Done" button item number        */
  35. #define    ADD_BTN        (12)            /* "Add" button item number            */
  36. #define    RM_BTN        (13)            /* "Remove" button item number         */
  37. #define LST_ITEM    (14)            /* my file list item number         */
  38. #define PROMPT_ITEM    (15)            /* prompt stattext item                */
  39.  
  40. #define SCRLSIZE    (15)            /* size of a scroll bar             */
  41.  
  42. #define LDEFID        (128)            /* res id of ldef                    */
  43. #define WDALRTID    (3999)            /* res id of working dir alert        */
  44. #define MFDLGID        (4000)            /* res id of multifile dialog        */
  45. #define MFSTRSID    (512)            /* res id of str# resource            */
  46.  
  47. #define ACTIVE        (0)                /* constants for hilitebtn            */
  48. #define INACTIVE    (255)
  49.  
  50. #define ENTER        (0x03)            /* charcode for the Enter key        */
  51.  
  52. #define FSFCBLen    (*((short *)(0x3f6)))        /* -1 if mfs, >0 if hfs    */
  53. #define SFSaveDisk    (*((short *)(0x214)))        /* -current vRefNum        */
  54. #define CurDirStore    (*((long *)(0x398)))        /* current dirID        */
  55.  
  56.  
  57. typedef struct fstruct {
  58.     OSType        ftype;            /* the file type                        */
  59.     short        vrefnum;        /* a vrefnum (or wdrefnum) for the file    */
  60.     short        fver;            /* the file's version                    */
  61.     String(63)    fname;            /* the file's name in Pascal form        */
  62. } FSTRUCT;
  63.  
  64. typedef FSTRUCT        (**FHNDL)[32767];    /* handle to an array of FSTRUCTs    */
  65.  
  66.  
  67. static int            nfiles;            /* number of files                            */
  68. static FHNDL        files;            /* handle to array of files                    */
  69. static DialogPtr    mfdlg;            /* the multifile dialog                        */
  70. static ListHandle    list;            /* my file name list                        */
  71. static SFReply        reply;            /* dummy reply struct for SFPGetFile        */
  72. static char            myprompt[256];    /* my internal copy of the prompt            */
  73. static Boolean        is_add;            /* true if Add button, false if Open button    */
  74. static Boolean        chklist;        /* true if we should check SF's selection    */
  75. static Boolean        good;            /* true if user hit done, false if cancel    */
  76. static char            openstr[256];    /* "Open"                                    */
  77. static char            addstr[256];    /* "Add"                                    */
  78.  
  79.  
  80.  
  81. /* center r in the screen 1/hfract across and 1/vfract down    */
  82. static void center(r, hfract, vfract)
  83. Rect    *r;
  84. int        hfract;
  85. int        vfract;
  86. {
  87.     int        h;
  88.     int        v;
  89.     
  90.     h = qd.screenBits.bounds.right - qd.screenBits.bounds.left;
  91.     v = qd.screenBits.bounds.bottom - qd.screenBits.bounds.top;
  92.     
  93.     h = h - r->right + r->left;
  94.     v = v - r->bottom + r->top;
  95.     
  96.     h = h / hfract;
  97.     v = v / vfract;
  98.     
  99.     SetRect(r, h, v, h + r->right - r->left, v + r->bottom - r->top);
  100. }
  101.  
  102. /* calculate the top left corner of the dialog    */
  103. void get_tl(where)
  104. Point    *where;
  105. {
  106.     DialogTHndl        dlgres;
  107.     Rect            drect;
  108.     
  109.     /* use default vals for classic mac screen if we can't get dialog    */
  110.     if (!(dlgres = GetResource('DLOG', MFDLGID))) {
  111.         where->h = 82;
  112.         where->v = 33;
  113.         return;
  114.     }
  115.     
  116.     drect = (*dlgres)->boundsRect;
  117.     center(&drect, 2, 3);
  118.     if (drect.top < 30)        /* keep the top of the dialog at least 10    */
  119.         drect.top = 30;        /* pixels below the menu bar.                */
  120.         
  121.     where->h = drect.left;
  122.     where->v = drect.top;
  123. }
  124.  
  125. /* start up the multifile dialog    */
  126. Boolean multifile(where, prompt, filefilt, numtypes, typelist)
  127. Point        *where;
  128. char        *prompt;
  129. ProcPtr        filefilt;
  130. int            numtypes;
  131. SFTypeList    *typelist;
  132. {
  133.     pascal short    dlg_hook();
  134.     pascal Boolean    filter();
  135.     
  136.     /* make a copy of the prompt so initdlg() can access it    */
  137.     strcpy(myprompt, prompt);
  138.     
  139.     GetIndString (openstr, MFSTRSID, 1);
  140.     if (strlen(openstr) == 0)
  141.         strcpy(openstr, "Open");
  142.     
  143.     GetIndString (addstr, MFSTRSID, 2);
  144.     if (strlen(addstr) == 0)
  145.         strcpy(addstr, "Add");
  146.     
  147.     is_add = true;
  148.     chklist = true;
  149.     
  150.     SFPGetFile(where, '', filefilt, numtypes, typelist, dlg_hook, &reply, MFDLGID, filter);
  151.     
  152.     return(good);
  153. }
  154.  
  155. /* return number of files collected    */
  156. int numfiles()
  157. {
  158.     return(nfiles);
  159. }
  160.  
  161. /* return file #fileno in fstrct    */
  162. void getfile(fileno, fstrct)
  163. int            fileno;                /* fileno is 1-based    */
  164. FSTRUCT        *fstrct;
  165. {
  166.     BlockMove(&(**files)[fileno-1], fstrct, sizeof(FSTRUCT));
  167. }
  168.  
  169. /* dispose of files memory    */
  170. void freemf()
  171. {
  172.     /* make sure we've got a valid handle    */
  173.     if (files)
  174.         DisposHandle(files);
  175. }
  176.  
  177. /************************************************************************
  178.                             utility routines
  179. ************************************************************************/
  180. /* flashes the button item #itemno in dlg for 4 ticks    */
  181. static void flashbtn(dlg, itemno)
  182. DialogPtr    dlg;
  183. int            itemno;
  184. {
  185.     short        itemtype;
  186.     Handle        item;
  187.     Rect        box;
  188.     long        finalticks;
  189.     
  190.     GetDItem(dlg, itemno, &itemtype, &item, &box);
  191.     
  192.     HiliteControl(item, inButton);
  193.     Delay(4, &finalticks);
  194.     HiliteControl(item, 0);
  195. }
  196.  
  197. /* returns the item rect of item #itemno in dlg    */
  198. static void getitemr(dlg, itemno, r)
  199. DialogPtr    dlg;
  200. int            itemno;
  201. Rect        *r;
  202. {
  203.     short    itemtype;
  204.     Handle    item;
  205.     
  206.     GetDItem(dlg, itemno, &itemtype, &item, r);
  207. }
  208.  
  209. /* calls ValidRect for the rect enclosing item #itemno in dlg.    */
  210. static void validitem(dlg, itemno)
  211. DialogPtr    dlg;
  212. int            itemno;
  213. {
  214.     Rect        itemr;
  215.     GrafPtr        oldport;
  216.     
  217.     /* make sure the port is correct before doing the ValidRect */
  218.     GetPort(&oldport);
  219.     SetPort(dlg);
  220.     
  221.     getitemr(dlg, itemno, &itemr);
  222.     ValidRect(&itemr);
  223.     
  224.     SetPort(oldport);
  225. }
  226.  
  227. /* returns the hiliting of the button item #itemno in dlg.    */
  228. static int gethilite(dlg, itemno)
  229. DialogPtr    dlg;
  230. int            itemno;
  231. {
  232.     short    itemtype;
  233.     Handle    item;
  234.     Rect    box;
  235.     
  236.     GetDItem(dlg, itemno, &itemtype, &item, &box);
  237.     return((*(ControlHandle)item)->contrlHilite);
  238. }
  239.  
  240. /* sets the hiliting of the button item #itemno in dlg to state.    */
  241. static void hilitebtn(dlg, itemno, state)
  242. DialogPtr    dlg;
  243. int            itemno;
  244. int            state;
  245. {
  246.     short    itemtype;
  247.     Handle    item;
  248.     Rect    box;
  249.     
  250.     GetDItem(dlg, itemno, &itemtype, &item, &box);
  251.     HiliteControl(item, state);
  252. }
  253.  
  254. /* sets the title of the Add button to title.    */
  255. static void set_title(dlg, title)
  256. DialogPtr    dlg;
  257. char        *title;
  258. {
  259.     short    itemtype;
  260.     Handle    item;
  261.     Rect    box;
  262.     
  263.     GetDItem(dlg, ADD_BTN, &itemtype, &item, &box);
  264.     SetCTitle(item, title);
  265. }
  266.  
  267. /* the tests used in noselect(), isfolder(), and isfile() come from    */
  268. /* "C Workshop" in the April '86 MacTutor.                            */
  269.  
  270. /* returns true if there's no selection in SF's list, false if there is    */
  271. static Boolean noselect()
  272. {
  273.     return((reply.fType == 0) && (reply.fName.length == 0));
  274. }
  275.  
  276. /* returns true if a folder is selected in SF's list, false if not    */
  277. static Boolean isfolder()
  278. {
  279.     return((reply.fType > 0) && (reply.fName.length == 0));
  280. }
  281.  
  282. /* returns true if a file is selected in SF's list, false if not    */
  283. static Boolean isfile()
  284. {
  285.     return((reply.fType > 0) && (reply.fName.length > 0));
  286. }
  287.  
  288. /* if mfs, simply returns the current vrefnum; if hfs, makes the current    */
  289. /* dir a working directory, and returns the wdrefnum, or 0 if the PBOpenWD    */
  290. /* failed because too many wd's were already open.                            */
  291. static short makewd()
  292. {
  293.     WDPBRec            wdpb;
  294.     HParamBlockRec    hpb;
  295.     
  296.     /* if running MFS    */
  297.     if (FSFCBLen == -1)
  298.         return(-SFSaveDisk);
  299.     
  300.     /* running HFS; check for an MFS volume; return vrefnum if so    */
  301.     hpb.volumeParam.ioCompletion = NULL;
  302.     hpb.volumeParam.ioNamePtr = NULL;
  303.     hpb.volumeParam.ioVRefNum = -SFSaveDisk;
  304.     hpb.volumeParam.ioVolIndex = NULL;
  305.     PBHGetVInfo(&hpb, false);
  306.     if (hpb.volumeParam.ioVSigWord == 0xd2d7)
  307.         return(-SFSaveDisk);
  308.     
  309.     /* make a new working directory    */
  310.     wdpb.ioCompletion = NULL;
  311.     wdpb.ioNamePtr = NULL;
  312.     wdpb.ioVRefNum = -SFSaveDisk;
  313.     wdpb.ioWDProcID = 'ERIK';
  314.     wdpb.ioWDDirID = CurDirStore;
  315.     
  316.     if (!PBOpenWD(&wdpb, false))
  317.         return(wdpb.ioVRefNum);
  318.     else
  319.         return(0);
  320. }
  321.  
  322. /* fill in an mfcell with data about a file    */
  323. static void mkcell(thecell, fname, ftype, fver, vrefnum, dirid)
  324. MFCELL            *thecell;
  325. String(63)        *fname;
  326. OSType            ftype;
  327. short            fver;
  328. short            vrefnum;
  329. long            dirid;
  330. {
  331.     int        namelen;
  332.     
  333.     thecell->ftype = ftype;
  334.     thecell->fver = fver;
  335.     thecell->vrefnum = vrefnum;
  336.     /* MFS doesn't use dirid's, so just use 0 instead    */
  337.     if (FSFCBLen > 0)
  338.         thecell->dirid = dirid;
  339.     else
  340.         thecell->dirid = 0;
  341.     namelen = fname->length;
  342.     BlockMove(fname, &thecell->fname, namelen + 1);
  343.     /* fill rest of fname with spaces    */
  344.     memset(thecell->fname.text + namelen, ' ', 63 - namelen);
  345. }
  346.  
  347. /* returns true if the file currently in the reply rec is already in my    */
  348. /* list, false if not. Also returns    in thecell the cell that the file    */
  349. /* is in, if it's in the list.                                            */
  350. static Boolean inlist(thecell)
  351. Cell    *thecell;
  352. {
  353.     MFCELL        thefile;
  354.     
  355.     /* build a cell with info about the current file    */
  356.     mkcell(&thefile, &reply.fName, reply.fType, reply.version, -SFSaveDisk, CurDirStore);
  357.     
  358.     /* look for the data    */
  359.     SetPt(thecell, 0, 0);
  360.     return(LSearch(&thefile, sizeof(MFCELL), NULL, thecell, list));
  361. }
  362.  
  363. /* unselects any selected cells, selects thecell, and scrolls to it    */
  364. static void sel1cell(thecell)
  365. Cell    *thecell;
  366. {
  367.     Cell    acell;
  368.     
  369.     /* unselect all selected cells. There must be a better way to do this.    */
  370.     SetPt (&acell, 0, 0);
  371.     while (LGetSelect(true, &acell, list)) {
  372.         LSetSelect(false, &acell, list);
  373.         acell.v += 1;
  374.     }
  375.     
  376.     LSetSelect(true, thecell, list);
  377.     LAutoScroll(list);
  378. }
  379.  
  380. /* adds fname to my list    */
  381. static void addfile(fname, ftype, fver, vrefnum, dirid, wdrefnum)
  382. String(63)    *fname;
  383. OSType        ftype;
  384. short        fver;
  385. short        vrefnum;
  386. long        dirid;
  387. short        wdrefnum;
  388. {
  389.     Cell        thecell;
  390.     MFCELL        newfile;
  391.     
  392.     /* add a new row at the bottom of the list    */
  393.     LDoDraw(false, list);
  394.     thecell.h = 0;
  395.     thecell.v = LAddRow(1, (*list)->dataBounds.bottom, list);
  396.     LDoDraw(true, list);
  397.     
  398.     /* build the new cell    */
  399.     mkcell(&newfile, fname, ftype, fver, vrefnum, dirid);
  400.     
  401.     /* add the new cell data    */
  402.     LSetCell(&newfile, sizeof(MFCELL), &thecell, list);
  403.     thecell.h = 1;
  404.     LSetCell(&wdrefnum, sizeof(short), &thecell, list);
  405.     
  406.     /* select the new cell    */
  407.     thecell.h = 0;
  408.     sel1cell(&thecell);
  409. }
  410.  
  411. /* remove file in thecell    */
  412. static void rmfile(thecell)
  413. Cell    *thecell;
  414. {
  415.     LDelRow(1, thecell->v, list);
  416. }
  417.  
  418. /************************************************************************
  419.                             dlog hook stuff
  420. ************************************************************************/
  421. static pascal short dlg_hook(item, dlg)
  422. short        item;
  423. DialogPtr    dlg;
  424. {
  425.     void    initdlg();
  426.     void    disposdlg();
  427.     void    chknmlist();
  428.     void    do_add();
  429.     void    do_rm();
  430.     
  431.     /* if a key is pressed, check SF's file list selection    */
  432.     if (item >= 1000) {
  433.         chklist = true;
  434.         return(item);
  435.     }
  436.     
  437.     switch (item) {
  438.         case INITMSG:
  439.             initdlg(dlg);
  440.             return(INITMSG);
  441.         case ADD_BTN:
  442.             if (isfile()) {
  443.                 do_add(dlg);
  444.                 return(ADD_BTN);
  445.             } else {                /* if we're not over a file, return        */
  446.                 chklist = true;        /* FOLDMSG since we're over a folder    */
  447.                 return(FOLDMSG);    /* and check the list next time            */
  448.             }
  449.         case RM_BTN:
  450.             do_rm(dlg);
  451.             return(RM_BTN);
  452.         case DONE_BTN:
  453.             disposdlg();
  454.             good = true;            /* return getCancel here to make sure    */
  455.             return(getCancel);        /* that SF doesn't make a wd for a file    */
  456.         case getOpen:
  457.             do_add(dlg);            /* item=getOpen on a double-click    */
  458.             return(NULLMSG);        /* or a press of Return or Enter    */
  459.         case getCancel:
  460.             disposdlg();
  461.             good = false;
  462.             return(getCancel);
  463.         case getNmList:
  464.             chknmlist(dlg);
  465.             return(getNmList);
  466.         case DIRMSG:                /* for all of these, we want to        */
  467.         case FOLDMSG:                /* check the file list next time    */
  468.         case GETDISK:                /* and return the same item number    */
  469.         case getEject:
  470.         case getDrive:
  471.             chklist = true;
  472.             return(item);
  473.         default:
  474.             return(item);
  475.     }
  476. }
  477.  
  478. /* init the dialog    */
  479. static void initdlg(dlg)
  480. DialogPtr    dlg;
  481. {
  482.     pascal void        userdraw();
  483.     
  484.     short            itemtype;
  485.     Handle            item;
  486.     Rect            box;
  487.     Rect            bounds;
  488.     Point            csize;
  489.     
  490.     /* save dialogptr    */
  491.     mfdlg = dlg;
  492.     
  493.     /* set the prompt text    */
  494.     GetDItem(dlg, PROMPT_ITEM, &itemtype, &item, &box);
  495.     SetIText(item, myprompt);
  496.     
  497.     /* get info about file list item and install user proc    */
  498.     GetDItem(dlg, LST_ITEM, &itemtype, &item, &box);
  499.     SetDItem(dlg, LST_ITEM, itemtype, userdraw, &box);
  500.     
  501.     /* make file list    */
  502.     box.right -= SCRLSIZE;
  503.     SetRect(&bounds, 0, 0, 2, 0);
  504.     SetPt (&csize, box.right - box.left, 16);
  505.     list = LNew(&box, &bounds, &csize, LDEFID, dlg, true, false, false, true);
  506.     
  507.     /* start with the Remove btn inactive, since there's nothing to remove    */
  508.     hilitebtn(dlg, RM_BTN, INACTIVE);
  509. }
  510.  
  511. /* copy list info to files, then dispose of list    */
  512. static void disposdlg()
  513. {
  514.     Cell        thecell;
  515.     MFCELL        data;
  516.     short        size;
  517.     short        wdrefnum;
  518.     int            i;
  519.     
  520.     nfiles = (*list)->dataBounds.bottom;
  521.     
  522.     /* if we can't get mem for file list, pretend there aren't any files    */
  523.     if (!(files = NewHandle(nfiles * sizeof(FSTRUCT))))
  524.         nfiles = 0;
  525.     
  526.     /* copy info from list into files    */
  527.     for (i = 0; i < nfiles; i++) {
  528.         /* get mfcell stuff from column 0    */
  529.         thecell.h = 0;
  530.         thecell.v = i;
  531.         size = sizeof(MFCELL);
  532.         LGetCell(&data, &size, &thecell, list);
  533.         
  534.         /* get wdrefnum from column 1    */
  535.         thecell.h = 1;
  536.         size = sizeof(short);
  537.         LGetCell(&wdrefnum, &size, &thecell, list);
  538.         
  539.         (**files)[i].ftype = data.ftype;
  540.         (**files)[i].vrefnum = wdrefnum;
  541.         (**files)[i].fver = data.fver;
  542.         BlockMove(&data.fname, &(**files)[i].fname, 64);
  543.     }
  544.     
  545.     LDispose(list);
  546. }
  547.  
  548. /* userproc to draw my file list and outline Add button    */
  549. static pascal void userdraw(wp, item)
  550. WindowPtr    wp;
  551. short        item;
  552. {
  553.     Rect        box;
  554.     
  555.     chklist = true;
  556.     
  557.     LUpdate(wp->visRgn, list);
  558.     
  559.     /* frame file list    */
  560.     getitemr(wp, LST_ITEM, &box);
  561.     box.right -= SCRLSIZE;
  562.     InsetRect(&box, -1, -1);
  563.     PenSize(1, 1);
  564.     FrameRect(&box);
  565.     
  566.     /* outline Add button    */
  567.     getitemr(wp, ADD_BTN, &box);
  568.     InsetRect(&box, -4, -4);
  569.     PenNormal();
  570.     PenSize(3, 3);
  571.     FrameRoundRect(&box, 16, 16);
  572. }
  573.  
  574. /* handle hits on the Add button    */
  575. static void do_add(dlg)
  576. DialogPtr    dlg;
  577. {
  578.     void        wdalert();
  579.     
  580.     short        wdrefnum;
  581.     Cell        thecell;
  582.     
  583.     if (!inlist(&thecell)) {
  584.         if (wdrefnum = makewd()) {
  585.             addfile(&reply.fName, reply.fType, reply.version, -SFSaveDisk, CurDirStore, wdrefnum);
  586.             hilitebtn(dlg, ADD_BTN, INACTIVE);
  587.             hilitebtn(dlg, RM_BTN, ACTIVE);
  588.         } else
  589.             wdalert(dlg);
  590.     }
  591. }
  592.  
  593. /* positions the wd alert so that it's just above and inside    */
  594. /* the sf dlg, and then displays it.                            */
  595. static void wdalert(dlg)
  596. DialogPtr    dlg;        /* the sfgetfile dlg    */
  597. {
  598.     int                width;            /* width of alert portrect    */
  599.     int                height;            /* height of alert portrect    */
  600.     AlertTHndl        alrtres;        /* handle to alert resource    */
  601.     Point            botleft;        /* botleft corner of dlg    */
  602.     GrafPtr            oldport;        /* save old port here        */
  603.     
  604.     /* init botleft in local coords    */
  605.     botleft.h = 0;
  606.     botleft.v = dlg->portRect.bottom;
  607.     
  608.     /* convert botleft to globals    */
  609.     GetPort(&oldport);
  610.     SetPort(dlg);
  611.     LocalToGlobal(&botleft);
  612.     SetPort(oldport);
  613.     
  614.     /* set the alert portrect. If we can't get the resource, bag    */
  615.     /* the alert and just beep.                                        */
  616.     if (alrtres = GetResource('ALRT', WDALRTID)) {
  617.         HNoPurge(alrtres);
  618.         width = (*alrtres)->boundsRect.right - (*alrtres)->boundsRect.left;
  619.         height = (*alrtres)->boundsRect.bottom - (*alrtres)->boundsRect.top;
  620.         (*alrtres)->boundsRect.top = botleft.v - height - 10;
  621.         (*alrtres)->boundsRect.left = botleft.h + 10;
  622.         (*alrtres)->boundsRect.bottom = botleft.v - 10;
  623.         (*alrtres)->boundsRect.right = botleft.h + width + 10;
  624.         
  625.         Alert(WDALRTID, NULL);
  626.     } else
  627.         SysBeep(5);
  628. }
  629.  
  630. /* handle hits on the Remove button    */
  631. static void do_rm(dlg)
  632. DialogPtr    dlg;
  633. {
  634.     void    chknmlist();
  635.     
  636.     Cell    thecell;
  637.     
  638.     SetPt(&thecell, 0, 0);
  639.     while (LGetSelect(true, &thecell, list)) {
  640.         rmfile(&thecell);
  641.         SetPt(&thecell, 0, 0);
  642.     }
  643.     
  644.     hilitebtn(dlg, RM_BTN, INACTIVE);
  645.     chknmlist(dlg);
  646. }
  647.  
  648. /* chknmlist is called after the selection in Standard File's list changes.    */
  649. /* I check to see what the current selection is in SF's list and set the    */
  650. /* title of my Add button appropriately.                                    */
  651. static void chknmlist(dlg)
  652. DialogPtr    dlg;
  653. {
  654.     Rect        btnrect;
  655.     GrafPtr        oldport;
  656.     Cell        thecell;    /* which cell in my list has SF's selection    */
  657.     Boolean        inmine;        /* true if selected cell in SF's list is    */
  658.                             /* also in mine    */
  659.     
  660.     /* if there's no selection, unhilite the add button                */
  661.     /* if the selection is a folder, change the title of the Add    */
  662.     /*         button to Open and make sure that it's active.            */
  663.     /* if the selection is a file, make sure that the title of the     */
  664.     /*        Add button is Add and that it's active.                    */
  665.     if (noselect())
  666.         hilitebtn(dlg, ADD_BTN, INACTIVE);
  667.     else if (isfolder()) {
  668.         if (gethilite(dlg, ADD_BTN) == INACTIVE)
  669.             hilitebtn(dlg, ADD_BTN, ACTIVE);
  670.         
  671.         if (is_add) {
  672.             set_title(dlg, openstr);
  673.             
  674.             /* changing the title generates an unnecessary update event. */
  675.             /* this gets rid of it.    */
  676.             validitem(dlg, ADD_BTN);
  677.             
  678.             is_add = false;
  679.         }
  680.     } else if (isfile()) {
  681.         /* if the selected file in SF's list is also in my list, select    */
  682.         /* the corresponding cell in my list and hilite the rm button    */
  683.         if ((inmine = inlist(&thecell)) == true) {
  684.             sel1cell(&thecell);
  685.             hilitebtn(dlg, RM_BTN, ACTIVE);
  686.         }
  687.         
  688.         /* if the file is in the list, make sure that the Add button is    */
  689.         /* inactive; otherwise, make sure that it's active.                */
  690.         if ((inmine) && (gethilite(dlg, ADD_BTN) == ACTIVE))
  691.             hilitebtn(dlg, ADD_BTN, INACTIVE);
  692.         else if ((!inmine) && (gethilite(dlg, ADD_BTN) == INACTIVE))
  693.             hilitebtn(dlg, ADD_BTN, ACTIVE);
  694.         
  695.         if (!is_add) {
  696.             set_title(dlg, addstr);
  697.             
  698.             /* bag the update event */
  699.             validitem(dlg, ADD_BTN);
  700.             
  701.             is_add = true;
  702.         }
  703.     }
  704. }
  705.  
  706. /************************************************************************
  707.                             filter stuff
  708. ************************************************************************/
  709. static pascal Boolean filter(dlg, event, itemhit)
  710. DialogPtr        dlg;
  711. EventRecord        *event;
  712. short            *itemhit;
  713. {
  714.     void        do_keydown();
  715.     void        do_mousedown();
  716.     
  717.     if (chklist) {
  718.         chknmlist(mfdlg);
  719.         chklist = false;
  720.     }
  721.     
  722.     switch (event->what) {
  723.         case mouseDown:
  724.             do_mousedown(dlg, event);
  725.             return(false);
  726.         case keyDown:
  727.         case autoKey:
  728.             do_keydown(dlg, event);
  729.             return(false);
  730.         default:
  731.             return(false);
  732.     }
  733. }
  734.  
  735. /* handle mouseDowns and track mouse if in list box    */
  736. static void do_mousedown(dlg, event)
  737. DialogPtr        dlg;
  738. EventRecord        *event;
  739. {
  740.     Rect        listr;
  741.     Point        locpt;
  742.     GrafPtr        oldport;
  743.     Cell        thecell;
  744.     
  745.     GetPort(&oldport);
  746.     SetPort(dlg);
  747.     
  748.     getitemr(dlg, LST_ITEM, &listr);
  749.     
  750.     locpt = event->where;
  751.     GlobalToLocal(&locpt);
  752.     if (PtInRect(&locpt, &listr)) {
  753.         LClick(&locpt, event->modifiers, list);
  754.         
  755.         /* hilite the remove button if there's a selection    */
  756.         SetPt(&thecell, 0, 0);
  757.         if (LGetSelect(true, &thecell, list))
  758.             hilitebtn(dlg, RM_BTN, ACTIVE);
  759.         else
  760.             hilitebtn(dlg, RM_BTN, INACTIVE);
  761.     }
  762.     
  763.     SetPort(oldport);
  764. }
  765.  
  766. /* flash Add/Open button if Return or Enter pressed    */
  767. static void do_keydown(dlg, event)
  768. DialogPtr        dlg;
  769. EventRecord        *event;
  770. {
  771.     int        c;
  772.     Cell    thecell;
  773.     
  774.     /* only flash Add button if we're on a file that isn't in my list,    */
  775.     /* or if we're on a folder.                                            */
  776.     c = event->message & charCodeMask;
  777.     if ((c == '\n') || (c == ENTER))
  778.         if ((isfile() && !inlist(&thecell)) || isfolder())
  779.             flashbtn(dlg, ADD_BTN);
  780. }